庫是寫好的現有的,成熟的,可以復用的二進制檔案。現實中每個專案都要依賴很多基礎的底層庫(iostream, vector, sys/resource.h ...),不可能每個人的程式都從零開始。
本質上來說庫是一種可執行的二進制形式,可以被作業系統載入記憶體中執行。庫常用的有兩種:靜態庫(Linux的 .a、Windows的 .lib)和動態庫(Linux的 .so、Windows的.dll)。
靜態庫(Static Library)是在編譯時期鏈接到程式的binarary file。
動態庫(Dynamic Library)在運行時才載入到記憶體中。
可以看到靜態庫包含在可執行文件中。而動態庫只需要在程序中創建一個符號表(庫代碼中引用的函數、變量),就可以在程式運作期間才載入到記憶體中。
使用靜態庫有兩個明顯的缺點:
1.增加應用程式的大小。如果應用程序包含多個執行文件,庫是可執行文件的一部分而浪費了太多空間。
2.修改/升級庫程式後,需要重新編譯/鏈接整個應用程式。這可能會對規模較大的專案造成編譯耗時的問題。
由於上述原因,通常在開發時會選擇動態庫而不是靜態庫。
但是,動態庫並不完美。對於開發人員來說,它們也有自己的缺陷——安裝時需要小心動態庫的安裝位置,以確保可執行文件能夠在運行時期找到鏈接庫。
定義:用來設定導出庫的路徑
定義:將原始碼編譯成不同格式的二進制檔
add_library(<name> [STATIC | SHARED] [<source>...])
<name> : 編譯出的庫名稱。
[STATIC | SHARED] : 指定生成庫的格式,是靜態庫或是動態庫。
[<source>...] : 原始碼名稱。
P.S. 編譯出的庫系統會自動加上名稱,靜態庫為 libname.a,動態庫為 libname.so
定義:將執行檔與二進制檔鍊結起來
target_link_libraries(<target> ... <item>...)
target : 執行檔名稱
item : 要與執行檔鍊結的二進制檔案名稱
$ git clone https://github.com/m11112089/2023_iT_CMake.git
$ cd ~/2023_iT_CMake/Day10
├── build
├── CMakeLists.txt
├── include
│ └── mysqrt.h
└── src
├── main.cpp
└── mysqrt.cpp
# 普通版本
include_directories(${PROJECT_SOURCE_DIR}/include)
add_executable(main src/main.cpp src/mysqrt.cpp)
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
# 設定庫的輸出路徑
# 靜態庫
add_library(mysqrt_a STATIC src/mysqrt.cpp)
target_include_directories(mysqrt_a PUBLIC ${PROJECT_SOURCE_DIR}/include)
add_executable(main_a src/main.cpp)
target_link_libraries(main_a mysqrt_a)
# 動態庫
add_library(mysqrt_so SHARED src/mysqrt.cpp)
target_include_directories(mysqrt_so PUBLIC ${PROJECT_SOURCE_DIR}/include)
add_executable(main_so src/main.cpp)
target_link_libraries(main_so mysqrt_so)
$ cd build
$ cmake ..
$ make
$ ./main 10
$ ./main_a 10
$ ./main_so 10
編譯完之後,在build資料夾會生成以下檔案。
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
├── lib
│ ├── libmysqrt_a.a
│ └── libmysqrt_so.so
├── main
├── main_a
├── main_so
└── Makefile
因為有設定過庫導出的位置(LIBRARY_OUTPUT_PATH)位於build/lib中,因此可以發現lib資料夾中有編譯出的libmysqrt_a.a靜態庫與libmysqrt_so.so與動態庫。
再來比較生成的執行檔大小,可以發現main和main_a檔案大小一模一樣都是24864 bytes,且比使用動態函式庫的main_so大。
這是因為動態庫與靜態庫的特性不同,靜態庫包含在可執行文件中。而動態庫只需要在程序運行期載入,因此執行檔案大小會不同。
接下來我們把build/lib資料夾中的靜態庫與動態庫刪除。
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
├── lib
├── main
├── main_a
├── main_so
└── Makefile
然後執行main_so會發現跳出錯誤訊息,提示找不到libmysqrt_so.so,但是執行main_a 卻能正常執行。
kai@esoc:~/2023_iT_CMake/Day10/build$ ./main_so 10
./main_so: error while loading shared libraries: libmysqrt_so.so: cannot open shared object file: No such file or directory
這是因為程式在執行期間才會去尋找動態庫載入到記憶體中。
這就是位什麼需要小心動態鏈接庫的安裝位置,以確保可執行文件能夠在運行時期找到鏈接庫。
C++ Development Tutorial 4: Static and Dynamic Libraries
書籍:程序员的自我修养—链接、装载与库